package threadpool;

import org.junit.Test;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * CompletableFuture 管理多个Future的结果
 * <p>
 * 参考文章：
 * https://www.jdon.com/50027
 */
public class T05_CompletableFutureTest {
    @Test
    public void test1() throws ExecutionException, InterruptedException {
        //通过CompletableFuture 提交三个任务 模拟分别下载A,B,C文件的任务
//        CompletableFuture<Long> future1 = CompletableFuture.supplyAsync(this::downloadAFile);
//        CompletableFuture<Long> future2 = CompletableFuture.supplyAsync(this::downloadBFile);
//        CompletableFuture<Long> future3 = CompletableFuture.supplyAsync(this::downloadCFile);

        //通过CompletableFuture控制 当三个任务都执行完毕后，才向下执行
//        CompletableFuture.allOf(future1,future2,future3).join();

        //通过CompletableFuture控制 只要有一个任务执行完毕，就向下执行
//        CompletableFuture.anyOf(future1,future2,future3).join();
//        System.out.println(future1.get());
//        System.out.println(future2.get());
//        System.out.println(future3.get());


        //在执行完某个任务后， 再去执行若干个回调函数，最后得到结果，这形成了一条调用链
//        CompletableFuture.supplyAsync(this::downloadAFile)
//                .thenApply((size)->size+"kb")
//                .thenApply((str)->str+"~~~")
//                .thenAccept(System.out::println);

        //  使用thenCompose，它能给一个Function返回CompletionStage。这将具有像flatMap一样的展平效果。
//       CompletableFuture.supplyAsync(this::downloadAFile)
//               .thenCompose(aLong -> CompletableFuture.supplyAsync(()->{
//                   System.out.println(456);
//                   return 11L;
//               }));

        //这段异步代码被提交给ForkJoinPool.commonPool()执行
        CompletableFuture<Long> completableFuture = CompletableFuture.supplyAsync(this::downloadAFile);
        //每一个apply的任务都会放入一个栈中，后被放入的任务将先得到执行
        //不带async的所有的回调  将被一个个从栈中取出在前一段 ，都在与它们的前任相同的线程上同步执行
        //带async的 每条消息都作为单独的任务提交给ForkJoinPool.commonPool()，回调方法执行是异步的，后者不会等待前者完成。
        //example：

        //同步版本：
//        completableFuture.thenApply((x)->{
//            System.out.println("aaa");
//            delay();
//            System.out.println("aaa end");
//            return 999L;
//        });
//        //需要等待上面任务完成
//        completableFuture.thenApply((x)->{
//            System.out.println("bbb");
//            delay();
//            System.out.println("bbb end");
//            return 200L;
//        });

        //异步版本：
//        completableFuture.thenApplyAsync((x)->{
//            System.out.println("aaa");
//            delay();
//            System.out.println("aaa end");
//            return 999L;
//        });
//        //不会等待上面任务完成
//        completableFuture.thenApplyAsync((x)->{
//            System.out.println("bbb");
//            delay();
//            System.out.println("bbb end");
//            return 200L;
//        });

        //处理异常
        //exceptionally 如果先前的计算失败并带有异常，则通过采用将执行的替代函数使我们有机会进行恢复回退。
        //回调会继续成功执行，只是输入参数是exceptionally执行的输出结果。

//        CompletableFuture.supplyAsync(this::makeError).exceptionally(ex->{
//            ex.printStackTrace();
//            return 0;
//        }).thenAccept(System.out::println);


        //如果一个的回调输入参数是需要依赖于两次计算结果，除了上面的将多个任务join， 还可以使用thenCombine 指定合并2个任务
//        CompletableFuture<Long> future1 = CompletableFuture.supplyAsync(this::downloadAFile);
//        CompletableFuture<Long> future2 = CompletableFuture.supplyAsync(this::downloadBFile);
//        future1.thenCombine(future2, Long::sum).thenAccept(System.out::println);

        //现在，当你只需要其中一个的结果时呢？
        CompletableFuture<Long> future1 = CompletableFuture.supplyAsync(this::downloadAFile);
        CompletableFuture<Long> future2 = CompletableFuture.supplyAsync(this::downloadBFile);
        future1.acceptEither(future2, System.out::println);

        while (true) {
        }

    }

    public Integer makeError() {
        throw new RuntimeException("ex");
    }

    public long downloadAFile() {
        System.out.println("start download~~~");
        delay();
        System.out.println("end download~~~");
        return 102400;
    }

    public long downloadBFile() {
        delay();
        return 204800;
    }

    public long downloadCFile() {
        delay();
        return 1980;
    }

    private void delay() {
        try {
//            Thread.sleep(new Random().nextInt(5000));
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
